package com.example.springboot.ytest.geek;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

/**
 * 第四课作业：思考有多少种方式，在 main 函数启动一个新线程，运行一个方法，拿到这
 * 个方法的返回值后，退出主线程? 写出你的方法，越多越好
* @author luffy
* @date 2021/4/25 21:12
**/
class Multithread {

    private static Long result;
    private static final long SLEEP_TIME = 512;

    static Long getResult() {
        return result;
    }

    /**
     * 存储线程开始时间
     */
    static void setResult(Long r) {
        result = r;
    }

    public static Long mainFun() {
        long startTime = System.currentTimeMillis();
        System.out.println("mainFun in: ");
        try {
            Thread.sleep(SLEEP_TIME);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("mainFun out: ");
        }

        return startTime;
    }

    public static void mainRun() {
        setResult(mainFun());
    }

    /**
     * Runable + join + Global variable
     */
    static void t0() throws InterruptedException {
        setResult(null);
        System.out.println("t0 in");
        Thread thread = new Thread(()->mainRun());
        thread.start();
        thread.join();
        System.out.println("t0 out: "+spendTimeStr());
    }

    /**
     * Runable + wait + Global variable
     */
    static void t1() throws InterruptedException {
        setResult(null);
        System.out.println("t1 in");
        Thread thread = new Thread(()->mainRun());
        thread.start();
        synchronized (thread) {
            thread.wait();
        }
        System.out.println("t1 out: "+spendTimeStr());
    }

    /**
     * Sleep + interrupt + Global variable
     */
    static void t2() {
        setResult(null);
        System.out.println("t2 in");
        Thread cur = Thread.currentThread();
        Thread thread = new Thread(()->paramRun(TYPE.SLEEP, cur));
        thread.start();
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            //e.printStackTrace();
            System.out.println(e.getMessage());
        }
        System.out.println("t2 isAlive: "+thread.isAlive());
        System.out.println("t2 out: "+spendTimeStr());
    }

    /**
     * while + sleep + Global variable
     */
    static void t3() throws InterruptedException {
        setResult(null);
        System.out.println("t3 in");
        Thread thread = new Thread(()->mainRun());
        thread.start();
        while (null == getResult()) {
            Thread.sleep(1);
        }
        System.out.println("t3 isAlive: "+thread.isAlive());
        System.out.println("t3 out: "+spendTimeStr());
    }

    /**
     * LockSupport + Global variable
     */
    static void t4() {
        setResult(null);
        System.out.println("t4 in");
        Thread tt = Thread.currentThread();
        /**
         * 一开始写成如下，其实 Thread.currentThread() 在子线程中才真正执行，拿到的就不是主线程的 Thread, 所以主线程一直会被卡住。
         * Thread td1 = new Thread(()->paramRun(Thread.currentThread()));
         * 也能看出 lambda 类似 C++ 中的宏
         * 感谢群里 Cc 兄的提醒 ^_^
         */
        Thread thread = new Thread(()->paramRun(TYPE.LOCK_SUPPORT, tt));
        thread.start();
        LockSupport.park();
        System.out.println("t4 isAlive: "+thread.isAlive());
        System.out.println("t4 out: "+spendTimeStr());
    }

    /**
     * Semaphore + Global variable
     */
    static void t5(){
        setResult(null);
        System.out.println("t5 in");
        Semaphore semaphore = new Semaphore(0);
        Thread thread = new Thread(()->paramRun(TYPE.SEMAPHORE, semaphore));
        thread.start();
        semaphore.acquireUninterruptibly();
        System.out.println("t5 isAlive: "+thread.isAlive());
        System.out.println("t5 out: "+spendTimeStr());
    }

    /**
     * CountdownLatch + Global variable
     */
    static void t6() throws InterruptedException {
        setResult(null);
        System.out.println("t6 in");
        CountDownLatch latch = new CountDownLatch(1);
        Thread thread = new Thread(()->paramRun(TYPE.COUNT_DOWN, latch));
        thread.start();
        latch.await();
        System.out.println("t6 isAlive: "+thread.isAlive());
        System.out.println("t6 out: "+spendTimeStr());
    }

    /**
     * CyclicBarrier + Global variable
     */
    static void t7() throws BrokenBarrierException, InterruptedException {
        setResult(null);
        System.out.println("t7 in");
        CyclicBarrier barrier = new CyclicBarrier(2);
        Thread thread = new Thread(()->paramRun(TYPE.CYCLIC_BAR, barrier));
        thread.start();
        barrier.await();
        System.out.println("t7 isAlive: "+thread.isAlive());
        System.out.println("t7 out: "+spendTimeStr());
    }

    /**
     * ExecutorService + Future + Callable
     */
    static void t8() throws ExecutionException, InterruptedException {
        System.out.println("t8 in");
        ExecutorService executorService = initThreadPoolExecutor();
        Future<Long> future1 = executorService.submit(()->mainFun());
        Long startTime = future1.get();
        System.out.println("t8 out: "+(System.currentTimeMillis()-startTime));
    }

    /**
     * FutureTask
     */
    static void t9() throws ExecutionException, InterruptedException {
        System.out.println("t9 in");
        FutureTask<Long> task = new FutureTask<>(() -> mainFun());
        new Thread(task).start();
        //第二种方方式
//        ExecutorService executor = Executors.newSingleThreadExecutor();
//        FutureTask<Long> task = new FutureTask<>(() -> mainFun());
//        executor.submit(task);

        Long startTime = task.get();
        System.out.println("t9 out: "+(System.currentTimeMillis()-startTime));
    }

    public static void main(String[] args) {
        try {
            t0();
            System.out.println("-----------------------------");
            t1();
            System.out.println("-----------------------------");
            t3();
            System.out.println("-----------------------------");
            t4();
            System.out.println("-----------------------------");
            t5();
            System.out.println("-----------------------------");
            t6();
            System.out.println("-----------------------------");
            t7();
            System.out.println("-----------------------------");
            t8();
            System.out.println("-----------------------------");
            t9();
            System.out.println("-----------------------------");
            t2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void paramRun(TYPE type, Object o) {
        mainRun();
        System.out.println("paramRun out");
        switch (type) {
            case LOCK_SUPPORT:
                LockSupport.unpark((Thread)o);
//            ((Thread) o).interrupt();
                break;
            case SEMAPHORE:
                ((Semaphore)o).release();
                break;
            case COUNT_DOWN:
                ((CountDownLatch)o).countDown();
                break;
            case CYCLIC_BAR:
                try {
                    ((CyclicBarrier)o).await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            case SLEEP:
                // TODO: interrupt 会影响之后的线程状态，虽然文档上说 interrupted 接口可以清除这状态，
                // 但经自测是会永远影响状态的，无法还原，即只要 interrupt() 过 LockSupport.part() 都是停不住的
                // 所以我的示例中这个操作(t2())只能放在最后
                ((Thread) o).interrupt();
                break;
        }
    }

    enum TYPE{
        LOCK_SUPPORT,
        SEMAPHORE,
        COUNT_DOWN,
        CYCLIC_BAR,
        SLEEP
    }

    public static ThreadPoolExecutor initThreadPoolExecutor() {
        int coreSize = Runtime.getRuntime().availableProcessors();
        int maxSize = Runtime.getRuntime().availableProcessors() * 2;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(500);
        CustomThreadFactory threadFactory = new CustomThreadFactory();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(coreSize, maxSize,
                1, TimeUnit.MINUTES, workQueue, threadFactory);
        return executor;
    }

    static String spendTimeStr() {
        return " spendTime: "+(System.currentTimeMillis()-getResult());
    }
}

class CustomThreadFactory implements ThreadFactory {
    private AtomicInteger serial = new AtomicInteger(0);
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r);
        thread.setDaemon(true); // 根据需要，设置守护线程
        thread.setName("CustomeThread-" + serial.getAndIncrement());
        return thread;
    }
}